<script setup>
import { useClipboard } from '@vueuse/core'
import * as regPresetsObj from '@/utils/regexp.js'
import RegularVisualization from './components/RegularVisualization'
import { parse } from 'regulex_common'

const { proxy } = getCurrentInstance()

// 正则表达式对象
const reg = ref(null)
// 正则表达式字符串
const regStr = ref('')
// 正则表达式修饰符
const regFlag = ref(['g'])
// 预设自动搜索
const auto = ref(true)
// 正则预设列表
const regPresets = ref([])
// 待匹配字符串
const matchStr = ref('')
// 匹配失败提示
const matchError = ref('')
// 可视化
const visualization = ref(true)
// 可视化组件实例
const regVisualizationRef = ref(null)

const { text, copy, copied, isSupported } = useClipboard({ source: reg })

watchEffect(() => {
    try {
        reg.value = parse(regStr.value)
            ? new RegExp(regStr.value, regFlag.value.join(''))
            : null
        matchError.value = []
    } catch (e) {
        if (e instanceof parse.RegexSyntaxError) {
            let msg = ['Error:' + e.message, '']
            if (typeof e.lastIndex === 'number') {
                msg.push(regStr.value)
                msg.push(new Array(e.lastIndex + 1).join("-") + '^')
            }
            matchError.value = msg
        } else {
            proxy.$modal.msgError(e)
        }
        reg.value = null
    }
});

// 正则预设查找方法
const regPresetSearch = (value, cb) => {
    const results = (value && auto.value)
        ? regPresets.value = Object.values(regPresetsObj)
            .filter(r => r.name.includes(value) || r.source.includes(value))
            .map(v => {
                return {
                    label: v.name,
                    regexp: v,
                    value: v.source
                }
            })
        : []
    // call callback function to return suggestion objects
    cb(results)
}

// 正则预设选中方法
const regPresetSelect = r => {
    let selected = regPresets.value.find(v => v.value === r)
    if (selected) regFlag.value = selected.regexp.flags.split('')
};

// 匹配结果
const matchingResults = computed(
    () => matchStr.value.match(reg.value)?.filter(v => v) || []
);

// 匹配结果格式化
const matchingFormat = computed(() => {
    let n = 0
    let res = '',
        str = matchStr.value
    matchingResults.value.forEach(v => {
        n++
        let matchStr = str.substr(0, v.length + str.indexOf(v))
        str = str.substr(v.length + str.indexOf(v))
        res += matchStr.replace(
            v,
            `<span class="matching matching${n % 2 === 0 ? 2 : 1}">${v}</span>`
        )
    })
    return (res + str).replace(/\n/g, '<br>')
})

// 导出正则可视化图片
const exportRegVisualizationToImg = () => {
    regVisualizationRef.value && regVisualizationRef.value?.exportImage()
};

// Copy正则到剪贴板
const regCopy = async () => {
    if (!isSupported.value) return proxy.$modal.msgWarning('不支持复制');
    await copy()
    proxy.$modal.msgSuccess('复制成功：' + text.value);
};
</script>
<template>
    <div class="app-container">
        <div class="regex-header-container">
            <el-tooltip :content="`点击${auto ? '关闭' : '开启'}预设正则自动匹配`" position="bottom">
                <el-button class="special-button" @click="auto = !auto">
                    <template #icon>
                        <el-icon :color="auto ? '#409EFC' : ''">
                            <MagicStick />
                        </el-icon>
                    </template>
                </el-button>
            </el-tooltip>

            <el-autocomplete class="autocomplete" v-model="regStr" :fetch-suggestions="regPresetSearch"
                @select="regPresetSelect" :placeholder="`请输入正则表达式${auto ? '或输入文字选择自动匹配的预设正则' : ''
                    }`" fit-input-width clearable>
                <template #prepend>
                    <el-tooltip :content="reg ? '正则表达式正确' : '正则表达式有误'" position="bottom">
                        <el-icon v-if="!reg" size="15" color="#c45656">
                            <BellFilled />
                        </el-icon>
                        <el-icon v-else size="15" color="#529b2e">
                            <BellFilled />
                        </el-icon>
                    </el-tooltip>
                    <span class="ml-5px">/</span>
                </template>
                <template #default="{ item }">
                    <div>
                        <span :style="{ color: '#e83e8c' }">{{ item.label }}</span> -
                        <span>{{ item.value }}</span>
                    </div>
                </template>
                <template #append>
                    <span> /{{ reg?.flags || regFlag?.join('') }}</span>
                </template>
            </el-autocomplete>

            <el-popover title="选择修饰符" placement="bottom" width="300">
                <template #reference>
                    <el-button class="special-button">
                        <template #icon>
                            <el-icon :color="regFlag.length > 0 ? '#409EFC' : ''">
                                <Filter />
                            </el-icon>
                        </template>
                    </el-button>
                </template>
                <div class="min-w-200px">
                    <el-checkbox-group v-model="regFlag" direction="vertical">
                        <el-checkbox value="g"> -g：全局匹配 </el-checkbox>
                        <el-checkbox value="i"> -i：忽略大小写</el-checkbox>
                        <el-checkbox value="m"> -m：多行匹配</el-checkbox>
                        <el-checkbox value="s"> -s：特殊字符. 包含换行符</el-checkbox>
                    </el-checkbox-group>
                </div>
            </el-popover>

            <el-tooltip :content="`点击${visualization ? '关闭' : '开启'}可视化解析`" position="bottom">
                <el-button class="special-button" @click="visualization = !visualization">
                    <template #icon>
                        <View v-if="visualization" />
                        <Hide v-else />
                    </template>
                </el-button>
            </el-tooltip>

            <el-tooltip v-if="visualization && reg" content="下载解析图片" position="bottom">
                <el-button class="special-button" @click="exportRegVisualizationToImg">
                    <template #icon>
                        <Download />
                    </template>
                </el-button>
            </el-tooltip>

            <el-tooltip v-if="reg" content="点击复制正则表达式" position="bottom">
                <el-button class="special-button" @click="regCopy">
                    <template #icon>
                        <DocumentCopy />
                    </template>
                </el-button>
            </el-tooltip>
        </div>
        <div class="regex-content-container">
            <div class="matching-visualization" v-if="matchError.length > 0">
                <div v-for="(res, key) in matchError" :key="key">{{ res }}</div>
            </div>
            <div v-if="visualization">
                <RegularVisualization ref="regVisualizationRef" v-model="reg" />
            </div>
            <el-input style="white-space: nowrap;" type="textarea" v-model="matchStr" default-value=""
                placeholder="请输入要匹配的字符串" :autosize="{ minRows: 6, maxRows: 10 }" />

            <div class="matching-formatter" v-html="matchingFormat || '无匹配结果'">
            </div>

            <div class="matching-results" v-if="matchingResults.length > 0">
                <div>共 {{ matchingResults.length }} 处匹配：</div>
                <div v-for="(res, i) of matchingResults" :key="i">{{ res }}</div>
            </div>
        </div>
    </div>
</template>
<style>
.special-button+.special-button {
    margin-left: 0px;
}


.special-button:focus,
.special-button:hover {
    color: initial !important;
    background-color: initial !important;
    border-color: var(--el-button-border-color) !important;
}

.regex-header-container {
    /* 防止换行 */
    white-space: nowrap;
    display: flex;
    /* 设置组件之间的间距 */
    margin-right: 10px;
}

.regex-header-container>*:last-child {
    /* 最后一个组件不设置右边距 */
    margin-right: 0;
}

.regex-header-container .autocomplete {
    width: 100%;
    margin-bottom: 10px;
}

.regex-content-container {
    margin: 0px 0px 10px 0px;

    .matching-visualization {
        padding: 5px 11px;
        border-radius: 4px;
        margin: 0px 0px 10px 0px;
        line-height: 1.5;
        border: 1px solid #ccc;
        background: #EEE;
        overflow: auto;
        font-family: monospace;
    }

    .matching-formatter {
        padding: 5px 11px;
        line-height: 1.5;
        border: 1px solid #ccc;
        border-radius: 4px;
        margin-top: 10px;
        min-height: 136px;
        max-height: 220px;
        font-size: 14px;
        overflow-y: auto;
        overflow-wrap: break-word;
        box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
    }

    .matching-results {
        padding: 12px;
        margin-top: 10px;
        font-size: 14px;
        padding-top: 12px;
        background-color: #f8f5f0;
        opacity: 1;
        overflow-y: auto;
    }
}

.matching1 {
    background: #cff5ff;
    box-shadow: 0 0 0 1px #09f;
}

.matching2 {
    background: #ffdfff;
    box-shadow: 0 0 0 1px #df56ad;
}
</style>